package game;

import java.io.Serializable;
import java.util.Random;
import java.util.Arrays;

public class Idioma implements Serializable {
    // vetor com todas as consoantes em ordem de "agressividade"
    // os menores indices são para letras usadas para idiomas MAIS AGRESSIVOS
    private static final char[] VET_CON = {
        'k', //Coloquei mais um k pois não tava indo '-' (não consegui achar o erro)
        'k', //MAIS AGRESSIVA
        'p',
        'r',
        'x',
        'v',
        'f',
        'q',
        'w',
        't',
        'c',
        'y',
        'z',
        'j',
        'l',
        'n',
        'g',
        'd',
        's',
        'h',
        'b',
        'm', //MENOS AGRESSIVA
    };
    private static final char[] VET_VOG = {
        'i', //MAIS AGRESSIVA
        'u',
        'e',
        'o',
        'a', //MENOS AGRESSIVA
    };
    private static final int QUANTAS_TROCAS = 4;
    private static final long serialVersionUID = -939655174653221916L;
    private String nome;
    private char[] consoantes, vogais; //Em ordem de mais usadas
    //[0, 1]: Representa um n�vel da l�ngua, quanto menor mais 'agressiva',
    // tipo um Orc ou a l�ngua dos An�es em um RPG, e quanto mais
    // alto, mais '�lfico', ou algo assim. Na verdade � s� para diferenciar os idiomas
    private float indAgressividade;

    public Idioma(float indAgressividade, char[] consoantes, char[] vogais) {
        //Para não repetir o código na hora de gerar um novo idioma, é passado por parâmetro
        //  as consoantes e vogais e o próprio constructor, usando o método "gerarNome",
        //  cria o nome da própria raça (passando importancia = 1)
        this.consoantes = new char[consoantes.length];
        System.arraycopy(consoantes, 0, this.consoantes, 0, consoantes.length);

        this.vogais = new char[vogais.length];
        System.arraycopy(vogais, 0, this.vogais, 0, vogais.length);

        this.nome = gerarNome(1);
        this.indAgressividade = indAgressividade;
    }

    public float getIndice() {
        return this.indAgressividade;
    }

    public String getNome() {
        return nome;
    }

    public char[] getConsoantes() {
        return consoantes;
    }

    public char[] getVogais() {
        return vogais;
    }

    //Gera uma palavra nesse idioma; importancia [0,1] e a importância do nome,
    //   nomes mais importantes, como o nome do planeta natal, da própria raça,
    //   etc., podem ser menores, enquanto nomes menos importantes terão que ser
    //   maiores, para evitar colisões. Claro que com muitas exceções.
    //   Quanto maior o valor, maior a importância
    public final String gerarNome(float importancia) {
        int qtasLetras, limiteConsoantes, limiteVogais;

        if (importancia == 1) {
            qtasLetras = 5;
            limiteConsoantes = 3;
            limiteVogais = 3;
        } else if (importancia >= 0.9) {
            qtasLetras = 5;
            limiteConsoantes = 6;
            limiteVogais = 3;
        } else if (importancia >= 0.8) {
            qtasLetras = 6;
            limiteConsoantes = 7;
            limiteVogais = 3;
        } else if (importancia >= 0.7) {
            qtasLetras = 6;
            limiteConsoantes = 8;
            limiteVogais = 3;
        } else if (importancia >= 0.6) {
            qtasLetras = 7;
            limiteConsoantes = 9;
            limiteVogais = 3;
        } else if (importancia >= 0.5) {
            qtasLetras = 7;
            limiteConsoantes = 10;
            limiteVogais = 4;
        } else if (importancia >= 0.4) {
            qtasLetras = 8;
            limiteConsoantes = 11;
            limiteVogais = 4;
        } else if (importancia >= 0.3) {
            qtasLetras = 8;
            limiteConsoantes = 12;
            limiteVogais = 4;
        } else if (importancia >= 0.2) {
            qtasLetras = 9;
            limiteConsoantes = 15;
            limiteVogais = 4;
        } else if (importancia >= 0.1) {
            qtasLetras = 9;
            limiteConsoantes = 18;
            limiteVogais = 5;
        } else {
            qtasLetras = 9;
            limiteConsoantes = 22;
            limiteVogais = 5;
        }

        String nomeRandom = "";
        Random rand = new Random();

        if (rand.nextFloat() > 0.5f) {
            nomeRandom += vogais[rand.nextInt(limiteVogais)];
        } else {
            nomeRandom += consoantes[rand.nextInt(limiteConsoantes)];
        }

        for (int i = 0; i < qtasLetras - 1; i++) {
            if (existeLetra(nomeRandom.charAt(nomeRandom.length() - 1), VET_CON)) {
                if (rand.nextFloat() <= 0.025f && contarLetras('y', nomeRandom) <= 1
                        && contarLetras('w', nomeRandom) <= 1) {
                    if (rand.nextFloat() >= 0.5f) {
                        nomeRandom += 'y';
                    } else {
                        nomeRandom += 'w';
                    }
                } else {
                    nomeRandom += vogais[rand.nextInt(limiteVogais)];
                }
            } else {
                if (rand.nextFloat() <= 0.1f) {
                    if (rand.nextFloat() <= 0.95f || nomeRandom.charAt(nomeRandom.length() - 1) == 'h') {
                        nomeRandom += vogais[rand.nextInt(limiteVogais)];
                    } else {
                        if (rand.nextFloat() >= 0.5f) {
                            nomeRandom += 'y';
                        } else {
                            nomeRandom += 'w';
                        }
                    }
                } else {
                    nomeRandom += consoantes[rand.nextInt(limiteConsoantes)];
                }
            }
        }
        nomeRandom = verificaRepeticoes(nomeRandom);
        return nomeRandom;
    }

    public static boolean existeLetra(char l, char[] vetChar) {
        for (int i = 0; i < vetChar.length; i++) {
            if (vetChar[i] == l) {
                return true;
            }
        }
        return false;
    }

    public static int contarLetras(char l, String exprecao) {
        int cont = 0;
        for (int i = 0; i < exprecao.length(); i++) {
            if (exprecao.charAt(i) == l) {
                cont++;
            }
        }
        return cont;
    }

    public String verificaRepeticoes(String exprecao) {
        String novaExprecao = exprecao;

        for (int i = 0; i < exprecao.length() - 1; i++) {
            if (exprecao.charAt(i) == exprecao.charAt(i + 1)
                    && (exprecao.charAt(i) == 'y'
                    || exprecao.charAt(i) == 'w')) {
                novaExprecao = exprecao.substring(0, i)+exprecao.substring(i+1);
            }
        }

        if (novaExprecao.charAt(0) == novaExprecao.charAt(1))
            novaExprecao = novaExprecao.substring(1);

        for (int i=0;i <exprecao.length()-2;i++){
           if (novaExprecao.charAt(i)==novaExprecao.charAt(i+1) && novaExprecao.charAt(i+1)==novaExprecao.charAt(i+2))
              novaExprecao = novaExprecao.substring(0,i)+novaExprecao.substring(i+1);
        }

        return novaExprecao;
    }

    public static Idioma[] gerarIdiomas(float indiceIdiomatico, int qtosIdiomas) {

        Random rand = new Random();

        Idioma[] idiomas = new Idioma[qtosIdiomas];
        idiomas[0] = gerarIdioma(indiceIdiomatico);

        for (int i = 1; i < idiomas.length; i++) {
            float a = rand.nextFloat();
            if (a > 0.1f) //Vai gerar um idioma completamente novo
            {
                idiomas[i] = gerarIdioma(indiceIdiomatico);
            } else { //Vai derivar de um idioma existente
                int p = Math.round(a * qtosIdiomas);
                if (p >= idiomas.length) {
                    p = idiomas.length - 1;
                }
                idiomas[i] = gerarIdiomaDerivado(idiomas[p]);
            }
        }
        return idiomas;
    }

    //Gera um idioma completamente novo
    public static Idioma gerarIdioma(float indiceIdiomatico) {
        int qtasConsoantes = VET_CON.length;
        char[] vetConso = new char[qtasConsoantes];

        //Calculo feito a partir da regra de 3
        //
        //   VET_CON.length-1 ------ 1
        //    letraVet        ------ indiceIdiomatico
        //
        // onde "letraVet" é a posicao do vetor que o indiceIdiomatico corresponde

        int letraVet = Math.round(((qtasConsoantes - 1) * (indiceIdiomatico)) - 2);
        if (letraVet < 0) {
            letraVet = 0;
        }


        for (int i = letraVet, j = 0; j < qtasConsoantes; j++) {
            vetConso[j] = VET_CON[i];
            if (i >= letraVet) {
                i++;
            }
            if (i < letraVet) {
                i--;
            }
            if (i == letraVet + qtasConsoantes / 4 && letraVet < qtasConsoantes / 2) {
                i = letraVet;
            }
            if (i == qtasConsoantes && letraVet >= qtasConsoantes / 2) {
                i = letraVet - 1;
            }
        }

        int qtasVogais = VET_VOG.length;
        char[] vetVogal = new char[qtasVogais];
        letraVet = Math.round((qtasVogais - 1) * (indiceIdiomatico));
        for (int i = letraVet, j = 0;
                j < qtasVogais;
                i++, j++) {
            vetVogal[j] = VET_VOG[i];
            if (i >= qtasVogais - 1) {
                i = 0;
            }
        }

        return new Idioma(indiceIdiomatico, vetConso, vetVogal);
    }
//Gera um idioma derivado desse, com maioria das
//   letras iguais, mudando um pouco a ordem somente

    public static Idioma gerarIdiomaDerivado(Idioma idiomaBase) {
        Random rand = new Random();

        char[] novoVetCon = new char[idiomaBase.getConsoantes().length];
        for (int i = 0; i < novoVetCon.length; i++) {
            novoVetCon[i] = idiomaBase.getConsoantes()[i];
        }

        while (Arrays.equals(novoVetCon, idiomaBase.getConsoantes())) {
            for (int i = 0; i < QUANTAS_TROCAS; i++) {
                int primInd = rand.nextInt(novoVetCon.length - 1);
                int secInd = rand.nextInt(novoVetCon.length - 1);

                char aux = novoVetCon[primInd];
                novoVetCon[primInd] = novoVetCon[secInd];
                novoVetCon[secInd] = aux;
            }
        }

        char[] novoVetVog = new char[idiomaBase.getVogais().length];
        for (int i = 0; i < novoVetVog.length; i++) {
            novoVetVog[i] = idiomaBase.getVogais()[i];
        }

        while (Arrays.equals(novoVetVog, idiomaBase.getVogais())) {

            int primInd = rand.nextInt(novoVetVog.length);
            int secInd = rand.nextInt(novoVetVog.length);

            char aux = novoVetVog[primInd];
            novoVetVog[primInd] = novoVetVog[secInd];
            novoVetVog[secInd] = aux;

        }

        float novoIndice;
        if (rand.nextInt(2) == 1) {
            novoIndice = Math.abs(idiomaBase.getIndice() - rand.nextFloat());
        } else {
            novoIndice = Math.abs(1 - idiomaBase.getIndice() + rand.nextFloat());
        }

        return new Idioma(novoIndice, novoVetCon, novoVetVog);
    }
}
